import datetime
import re

from django.db.models import Sum
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator
from django.utils.dateparse import parse_date
from rest_framework import generics, mixins
from rest_framework.decorators import api_view
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from mongolog.models import (
    CASLog, StrategyLog, AppsLog,
    EmailLog, FileLog,
    DynamicAttackLog, BasicSafetyLog, LoopholesRiskLog, ProgramsRiskLog, AttackDetectionLog
)
from strategy.filters import StrategyApplyRecordFilter
from strategy.models import StrategyApplyRecord
from userdata.models import DailySummary
from zmad.models import ZTPUser, ZTPDevice
from asset.models import DeviceInfo, Device
from mongolog.utils import date_range, date_eq
from tools.datetime import range_date, date_filter, as_datetime
from .serializers import asset_heat_serializer, asset_trend_serializer, TerminalMalwareTrendSerializer
from ztp_backend.settings import WITH_WX, APPS_DANGER_TYPE_MAP
from pymongo.errors import OperationFailure
import traceback
# Create your views here.


@api_view(['GET'])
def user_overview(request):
    """用户情况总览"""
    date_from, date_to = date_filter(request.query_params)
    # 计算今日用户异常，活跃总览
    date_today = date_to
    active_users = set(CASLog.objects.mongo_distinct(
        'username',
        {
            **date_eq('log_time', date_today),
            'auth_code': 7,
            'username': {
                '$exists': True,
                '$ne': ''
            }
        }
    ))
    abnormal_users = set(StrategyLog.objects.mongo_distinct(
        'username',
        {
            'username': {
                '$exists': True,
                '$ne': ''
            },
            **date_eq('log_time', date_today),
            "uid": {"$ne": "STG/AUTH-3"}
        }
    ))
    # 用户总数包括禁用用户
    try:
        if WITH_WX:
            total = ZTPUser.objects.exclude(wx_user_id="").count()
        else:
            total = ZTPUser.objects.all().count()
    except:
        traceback.print_exc()
        total = None
    queryset = DailySummary.objects.values_list('date', 'active', 'inactive', 'abnormal')
    if date_from:
        queryset = queryset.filter(date__gte=date_from)
    if date_to:
        queryset = queryset.filter(date__lte=date_to)

    fields = ['date', '活跃用户数', '非活跃用户数', '行为异常用户数']
    trend = [dict(zip(fields, values)) for values in queryset]
    trend.append({'date': date_today, '活跃用户数': len(active_users), '非活跃用户数': total-len(active_users),
                  '行为异常用户数': len(abnormal_users)})
    data = {'fields': fields[1:], 'trend': trend, 'users': total}
    return Response(data)


@method_decorator(cache_page(60 * 30), name='get')
class StrategyOverviewView(generics.GenericAPIView):
    """策略情况总览
    策略触发总数 + 策略下发总数
    """
    queryset = StrategyApplyRecord.objects.all()
    filter_backends = (DjangoFilterBackend,)
    filterset_class = StrategyApplyRecordFilter

    def get(self, request, *args, **kwargs):
        date_from, date_to = date_filter(request.query_params)
        date_from = as_datetime(date_from)
        date_to = as_datetime(date_to)
        data = {
            'trigger_total': StrategyLog.objects.filter(log_time__gte=date_from, log_time_lt=date_to).count(),
            'dispatch_total': self.filter_queryset(self.get_queryset()).count()
        }
        return Response(data)


@cache_page(60 * 30)
@api_view(['GET'])
def strategy_trend(request):
    """策略触发趋势
    只统计每日策略触发总数，不区分策略类别，移动终端用
    """
    date_from, date_to = date_filter(request.query_params)
    aggregation = [
        {
            '$group': {
                '_id': '$date',
                'count': {'$sum': 1}
            },
        }
    ]
    match = date_range('log_time', date_from, date_to)
    if match:
        aggregation.insert(0, {'$match': match})
    queryset = list(StrategyLog.objects.mongo_aggregate(aggregation))
    # 找出起止日期
    # 如果提供了date_from/date_to，则展示的起/止日期为date_from/date_to，
    # 否则，展示的起/止日期为有记录以来的最远日期/今日
    date2count = {parse_date(agg['_id']): agg['count'] for agg in queryset if agg.get('_id')}
    if date2count:
        date_from = date_from or min(date2count.keys())
        date_to = date_to or datetime.date.today()
    else:
        date_from = date_to = datetime.date.today()
    # 组装数据
    trend = {date: 0 for date in range_date(date_from, date_to)}
    trend.update(date2count)

    data = [{date.strftime('%Y-%m-%d'): count} for date, count in trend.items()]
    return Response(data)


@api_view(['GET'])
def terminal_count(request):
    # 企业终端总数
    terminal_total = DeviceInfo.objects.count()
    # 风险终端总数
    terminal_risk = DeviceInfo.objects.filter(is_secure=False).count()
    data = {'total': terminal_total, 'risk': terminal_risk}
    return Response(data)


# @cache_page(60 * 30)
# @api_view(['GET'])
# def terminal_risk_dist(request):
#     """企业终端风险分类统计"""
#     data = get_app_risk_dist()
#     pc_risk_count = get_pc_risk_dist()
#     data[-1].update(pc_risk_count)
#     return Response(data)


# @cache_page(60 * 30)
@api_view(['GET'])
def terminal_risk_dist_0(request):
    """企业终端风险分类统计"""
    data = dict()
    # PC 端统计
    pc_total = Device.objects.filter(platform=2).count()
    pc_risk = Device.objects.filter(platform=2, is_secure=False).count()
    data['pc'] = {'count': pc_total, 'risk': pc_risk}
    # APP 端统计
    app_total = Device.objects.filter(platform=1).count()
    app_risk = Device.objects.filter(platform=1, is_secure=False).count()
    data['app'] = {'count': app_total, 'risk': app_risk}
    # Linux 端统计
    linux_total = Device.objects.filter(platform=4).count()
    linux_risk = Device.objects.filter(platform=4).exclude(risk_list=[]).count()
    data['linux'] = {'count': linux_total, 'risk': linux_risk}

    return Response(data)


@cache_page(60 * 30)
@api_view(['GET'])
def app_risk_dist(request):
    """移动终端风险分类统计
    """
    # 组装数据
    def get_dict_data(query_i, app_type):
        keys_list = ['md5', 'package_name', 'program_name', 'apk_path', 'virus_name', 'virus_description',
                     'apk_version_name', 'apk_size', 'is_system_apps']
        data_list = []
        if query_i[app_type]:
            for i in range(len(query_i[app_type])):
                # 对缺少某些字段数据进行补齐处理
                for keys_ in keys_list:
                    if keys_ not in list(query_i[app_type][i].keys()):
                        query_i[app_type][i][keys_] = None
                tuple_ = (query_i['device_uuid'], dict(query_i[app_type][i])["virus_name"])  # tuple 格式，后面方便去重
                if tuple_ not in data_list:
                    data_list.append(tuple_)
        return data_list

    app_query_mongo = AppsLog.objects.mongo_aggregate([
        {
            '$match': {
                'device_uuid': {
                    '$exists': True,
                    '$ne': ''
                    },
            }
        },
        {
            '$project': {
                'date': 1,
                'device_uuid': 1,
                'apps_risk': 1,
                'apps_danger': 1,
                "log_time": 1
            }
        },
        {
            "$sort": {"device_uuid": 1, "log_time": -1}
        },
    ])

    apps_risk_list = list()
    for app_query_i in app_query_mongo:
        if 'apps_risk' in list(app_query_i.keys()) and app_query_i['apps_risk']:
            apps_risk_list.extend(get_dict_data(app_query_i, 'apps_risk'))
        if 'apps_danger' in list(app_query_i.keys()) and app_query_i['apps_danger']:
            apps_risk_list.extend(get_dict_data(app_query_i, 'apps_danger'))
    apps_risk_list = list(set(apps_risk_list))

    risk_device_dict = dict()
    risk_type_zh_list = list(APPS_DANGER_TYPE_MAP.values())
    risk_type_en_list = list(APPS_DANGER_TYPE_MAP.keys())
    for risk_type_zh_ in risk_type_zh_list:
        risk_device_dict[risk_type_zh_] = list()

    for apps_risk_ in apps_risk_list:
        risk_type_list = re.findall(r'\[(.*?)\]', apps_risk_[1])[0].split(',')  # 示例: ['prv', 'rmt', 'spy']
        for risk_type_ in risk_type_list:
            if risk_type_ in risk_type_en_list:
                risk_type_zh = APPS_DANGER_TYPE_MAP[risk_type_]  # 中文表示的应用类型
                if apps_risk_[0] not in risk_device_dict[risk_type_zh]:
                    risk_device_dict[risk_type_zh].append(apps_risk_[0])

    data = list()  # 返回的数据
    risk_total = 0
    for risk_type_zh_ in risk_type_zh_list:
        data.append({'field': risk_type_zh_, 'count': len(risk_device_dict[risk_type_zh_])})
        risk_total += len(risk_device_dict[risk_type_zh_])
    for d in data:
        d['ratio'] = round(d['count'] / risk_total if risk_total else 0, 2)

    return Response(data)


@cache_page(60 * 30)
@api_view(['GET'])
def app_risk_count(request):
    """移动终端风险类型统计
    """
    data_dict = dict()
    app_query_mongo = AppsLog.objects.mongo_aggregate([
        {
            '$match': {
                'device_uuid': {
                    '$exists': True,
                    '$ne': ''
                },
            }
        },
        {
            '$project': {
                'date': 1,
                'device_uuid': 1,
                'apps_risk': 1,
                'apps_danger': 1,
                "log_time": 1
            }
        },
    ])

    # 组装数据
    device_list = list()
    for app_query_i in app_query_mongo:
        if 'apps_risk' in list(app_query_i.keys()) and app_query_i['apps_risk']:
            device_list.append(app_query_i['device_uuid'])
        if 'apps_danger' in list(app_query_i.keys()) and app_query_i['apps_danger']:
            device_list.append(app_query_i['device_uuid'])

    data_dict['恶意代码'] = len(set(device_list))
    data_dict['恶意URL'] = 0
    data_dict['恶意WIFI'] = 0
    total_num = data_dict['恶意代码'] + data_dict['恶意URL'] + data_dict['恶意WIFI']
    data_list = list()
    for key, value in data_dict.items():
        data_list.append({'field': key, 'count': value, 'ratio': round(value / total_num if total_num else 0, 2)})

    return Response(data_list)


# @cache_page(60 * 30)
@api_view(['GET'])
def linux_risk_dist(request):
    """移动终端风险分类统计
    获取统计数据方式：
    1，通过Device表，基于device_uuid,risk_list 两字段，因为展示的内容，可由策略来关联
    2，通过读取mongo相关4张表，进行统计
    """
    risk_device = Device.objects.filter(platform=4).exclude(risk_list=[]).all()
    l1, l2, l3, l4 = list(), list(), list(), list()
    for risk_device_i in risk_device:
        print("risk_device_i : ", risk_device_i.risk_list)
        if "安全基线检测" in risk_device_i.risk_list:
            l1.append(risk_device_i.device_uuid)
        if "持续漏洞检测" in risk_device_i.risk_list:
            l2.append(risk_device_i.device_uuid)
        if "持续恶意代码检测" in risk_device_i.risk_list:
            l3.append(risk_device_i.device_uuid)
        if "持续入侵检测" in risk_device_i.risk_list:
            l4.append(risk_device_i.device_uuid)

    data_dict = {
        "安全基线": len(l1),
        "漏洞风险": len(l2),
        "恶意代码": len(l3),
        "入侵风险": len(l4)
    }
    data = list()  # 返回的数据
    for key, value in data_dict.items():
        data.append({'field': key, 'count': value})

    return Response(data)


@cache_page(60 * 30)
@api_view(['GET'])
def linux_risk_count(request):
    """移动终端风险分类统计
    获取统计数据方式：
    1，通过Device表，基于device_uuid,risk_list 两字段，因为展示的内容，可由策略来关联
    2，通过读取mongo相关4张表，进行统计
    """
    data = dict()
    linux_query_mongo = ProgramsRiskLog.objects.mongo_aggregate([
        {
            '$match': {
                'device_uuid': {
                    '$exists': True,
                    '$ne': ''
                },
            }
        },
        {
            '$project': {
                'device_uuid': 1,
                'date': 1,
                'trojans': 1,
                'infection': 1,
                'worm': 1,
                'hack': 1,
                'grey': 1,
                'risk': 1,
                'rubbish': 1,
                'others': 1,
                "log_time": 1
            }
        },
    ])

    # 组装数据
    l1, l2, l3, l4, l5, l6, l7, l8 = list(), list(), list(), list(), list(), list(), list(), list()
    for linux_query_i in linux_query_mongo:
        if linux_query_i['trojans']:
            l1.append(linux_query_i['device_uuid'])
        if linux_query_i['infection']:
            l2.append(linux_query_i['device_uuid'])
        if linux_query_i['worm']:
            l3.append(linux_query_i['device_uuid'])
        if linux_query_i['hack']:
            l4.append(linux_query_i['device_uuid'])
        if linux_query_i['grey']:
            l5.append(linux_query_i['device_uuid'])
        if linux_query_i['risk']:
            l6.append(linux_query_i['device_uuid'])
        if linux_query_i['rubbish']:
            l7.append(linux_query_i['device_uuid'])
        if linux_query_i['others']:
            l8.append(linux_query_i['device_uuid'])

    data['木马'] = len(set(l1))
    data['感染式恶意代码'] = len(set(l2))
    data['蠕虫'] = len(set(l3))
    data['黑客工具'] = len(set(l4))
    data['灰色软件'] = len(set(l5))
    data['风险软件'] = len(set(l6))
    data['垃圾软件'] = len(set(l7))
    data['其他病毒'] = len(set(l8))
    total_num = data['木马'] + data['感染式恶意代码'] + data['蠕虫'] + data['黑客工具'] + data['灰色软件'] + data['风险软件']\
                + data['垃圾软件'] + data['其他病毒']
    data_list = list()
    for key, value in data.items():
        data_list.append({'field': key, 'count': value, 'ratio': round(value / total_num if total_num else 0, 2)})

    return Response(data_list)


def terminal_dist_to_display(static_dist, dynamic_dist):
    """
    XXX 产品要求此处无论有无数据都要固定展示下列7项终端威胁，且名称写死
    1. 恶意代码 = 恶意APK
    2. 恶意URL = 恶意短信钓鱼URL + 访问钓鱼URL
    3. 恶意WIFI = 恶意WiFi
    4. 非法篡改 = 开发者签名被篡改 + APK被篡改
    5. 非法调试 = 调试攻击
    6. 非法注入 = 注入攻击
    7. 内存dump = 内存dump

    改动策略名称将导致此函数工作不正常！
    """
    display_dist = dict([(r, 0) for r in ('恶意代码', '恶意URL', '恶意WIFI', '非法篡改', '非法调试', '非法注入', '内存dump')])

    for r in static_dist:
        if '终端检出恶意apk' in r['field']:
            display_field = '恶意代码'
        elif r['field'] in ['检出短信钓鱼URL', '检出访问钓鱼URL']:
            display_field = '恶意URL'
        elif r['field'] == '检出访问恶意WiFi':
            display_field = '恶意WIFI'
        else:
            display_field = r['field']
        display_dist[display_field] = display_dist.get(display_field, 0) + r['count']

    for r in dynamic_dist:
        if r['field'] in ['开发者签名被篡改', 'APK被篡改']:
            display_field = '非法篡改'
        elif r['field'] == '调试攻击':
            display_field = '非法调试'
        elif r['field'] == '注入攻击':
            display_field = '非法注入'
        else:
            display_field = r['field']
        display_dist[display_field] = display_dist.get(display_field, 0) + r['count']

    return [{'field': k, 'count': v} for k, v in display_dist.items()]


class TerminalMalwareTrend(mixins.ListModelMixin,
                           generics.GenericAPIView):
    """终端恶意/风险/敏感应用趋势"""
    serializer_class = TerminalMalwareTrendSerializer

    def get_queryset(self):
        date_from, date_to = date_filter(self.request.query_params)
        queryset = DeviceInfo.objects.values('date').annotate(
            danger=Sum('danger_app'),
            risk=Sum('risk_app'),
            sensitive=Sum('sensitive_app')
        ).values('date', 'danger', 'risk', 'sensitive')
        if date_from:
            queryset = queryset.filter(date__gte=date_from)
        if date_to:
            queryset = queryset.filter(date__lte=date_to)
        return queryset

    @method_decorator(cache_page(60 * 30))
    def get(self, request, *args, **kwargs):
        ret = self.list(request, *args, **kwargs)
        from django.db import connection
        print(connection.queries)
        return ret


@cache_page(60 * 30)
@api_view(['GET'])
def asset_heat(request):
    """资产热度图"""

    # 应用访问TOP5
    app_visit = asset_heat_serializer(CASLog, 'resource')
    # 邮件附件TOP5
    email_attachment = asset_heat_serializer(EmailLog, 'filenames')
    # 文件下载TOP5
    file_download = asset_heat_serializer(FileLog, 'filename', source=FileLog.SOURCE_DOWNLOAD)
    # 文件分享TOP5
    file_share = asset_heat_serializer(FileLog, 'filename', source=FileLog.SOURCE_SHARE)

    assets = {
        '应用访问热度': app_visit,
        '邮件附件热度': email_attachment,
        '文件下载热度': file_download,
        '文件分享热度': file_share,
    }
    data = [{'asset': k, 'heat': v} for k, v in assets.items()]
    return Response(data)


@cache_page(60 * 30)
@api_view(['GET'])
def asset_trend(request):
    """资产趋势图"""
    date_from, date_to = date_filter(request.query_params)

    # 应用访问趋势
    app_visit_trend = asset_trend_serializer(CASLog, date_from, date_to)
    # 邮件收发趋势
    email_trend = asset_trend_serializer(EmailLog, date_from, date_to)
    # 文件下载趋势
    file_download_trend = asset_trend_serializer(FileLog, date_from, date_to, source=FileLog.SOURCE_DOWNLOAD)
    # 文件分享趋势
    file_share_trend = asset_trend_serializer(FileLog, date_from, date_to, source=FileLog.SOURCE_SHARE)

    # 找出起止日期
    # 如果提供了date_from/date_to，则展示的起/止日期为date_from/date_to，
    # 否则，展示的起/止日期为有记录以来的最远日期/今日
    date_set = {*app_visit_trend.keys(),
                *email_trend.keys(),
                *file_download_trend.keys(),
                *file_share_trend.keys()}
    if date_set:
        date_from = date_from or min(date_set)
        date_to = date_to or datetime.date.today()
    else:
        date_from = date_to = datetime.date.today()

    # 组装数据
    trend_fields = ['应用访问', '邮件收发', '文件下载', '文件分享']
    trend = {date: dict(date=date.strftime('%Y-%m-%d'), **{f: 0 for f in trend_fields})
             for date in range_date(date_from, date_to)}
    for date, count in app_visit_trend.items():
        trend[date]['应用访问'] = count
    for date, count in email_trend.items():
        trend[date]['邮件收发'] = count
    for date, count in file_download_trend.items():
        trend[date]['文件下载'] = count
    for date, count in file_share_trend.items():
        trend[date]['文件分享'] = count

    data = {'fields': trend_fields, 'trend': list(trend.values())}
    return Response(data)


@cache_page(60 * 30)
@api_view(['GET'])
def strategy_top(request):
    """策略触发排行Top10"""
    queryset = StrategyLog.objects.mongo_aggregate([
        {
            '$group': {
                '_id': '$meta.name',
                'weight': {'$first': '$meta.weight'},
                'level': {'$first': '$meta.level'},
                'count': {'$sum': 1}
            },
        },
        {'$limit': 10}
    ])

    data = [{
        'name': agg['_id'],
        'count': agg['count'],
        'weight': agg['weight'],
        'level': agg['level'],
    } for agg in queryset]
    return Response(data)


@cache_page(60 * 30)
@api_view(['GET'])
def file_download_ranking(request):
    """文件下载排行"""
    top10_queryset = FileLog.objects.mongo_aggregate([
        {'$match': {'source': FileLog.SOURCE_DOWNLOAD}},
        {'$sortByCount': '$filename'},
        {'$limit': 10}
    ])

    data = [{'field': agg['_id'], 'count': agg['count']} for agg in top10_queryset]
    return Response(data)


@cache_page(60 * 30)
@api_view(['GET'])
def file_share_ranking(request):
    """文件分享排行"""
    top10_queryset = FileLog.objects.mongo_aggregate([
        {'$match': {'source': FileLog.SOURCE_SHARE}},
        {'$sortByCount': '$filename'},
        {'$limit': 10}
    ])

    data = [{'field': agg['_id'], 'count': agg['count']} for agg in top10_queryset]
    return Response(data)
